#ifndef GRAMMAR_CPP
#define GRAMMAR_CPP

#include "Grammar.h"
#include "GOAL.h"
#include "Code_Line.h"

std::map <std::string,bool> GOAL::Grammar::lhs_types;
std::map <std::string,bool> GOAL::Grammar::rhs_types;
std::map <std::string,bool> GOAL::Grammar::assignment_types;
std::map <std::string,bool> GOAL::Grammar::accumulator_types;
std::map <std::string,bool> GOAL::Grammar::connection_types;
std::map <std::string,bool> GOAL::Grammar::boolean_operator_types;
std::map <std::string,bool> GOAL::Grammar::standalone_types;
std::map <std::string,bool> GOAL::Grammar::output_types;
std::map <std::string,bool> GOAL::Grammar::input_types;

bool GOAL::Grammar::initialized_ = false;

GOAL::Grammar::Info::Info (Code_Line& code_line)
: confidence (0), pos (code_line.end ())
{

}

GOAL::Grammar::Info::Info (Info & info)
: confidence (info.confidence), pos (info.pos)
{

}

GOAL::Grammar::RegularExpression::RegularExpression (Code_Line &code_line)
: code_line_ (code_line)
{

}

GOAL::Grammar::Assignment::Assignment (Code_Line &code_line, Code_Line::iterator assign)
: RegularExpression (code_line), assign_ (assign)
{

}


GOAL::Grammar::ConsolePrint::ConsolePrint (Code_Line &code_line, Code_Line::iterator assign)
: Assignment (code_line,assign)
{

}

GOAL::Grammar::InputLine::InputLine (Code_Line &code_line, Code_Line::iterator assign)
: Assignment (code_line,assign)
{

}

void GOAL::Grammar::init (void)
{
  if (!initialized_)
    {
       // left hand side types
      lhs_types["Variable"] = true;
      lhs_types["Console"] = true;
      lhs_types["File"] = true;
      lhs_types["Brackets"] = true;
       
      // right hand side types
      rhs_types["Not"] = true;
      rhs_types["Plus"] = true;
      rhs_types["Minus"] = true;
      rhs_types["Times"] = true;
      rhs_types["Divide"] = true;
      rhs_types["Modulus"] = true;
      rhs_types["FunctionCall"] = true;
      rhs_types["Const"] = true;

      // rhs - variables and variable scope modifiers (indexers)
      rhs_types["Variable"] = true;
      rhs_types["Brackets"] = true;

      // rhs - boolean operations
      rhs_types["And"] = true;
      rhs_types["Or"] = true;
      rhs_types["Not"] = true;
      rhs_types["Equal"] = true;
      rhs_types["NotEqual"] = true;
      rhs_types["LessThan"] = true;
      rhs_types["LessThanOrEqual"] = true;
      rhs_types["GreaterThan"] = true;
      rhs_types["GreaterThanOrEqual"] = true;

      // rhs - IO operations
      rhs_types["File"] = true;
      rhs_types["Keyboard"] = true;

      // input types - IO operations
      input_types["File"] = true;
      input_types["Keyboard"] = true;

      // output types - IO operations
      output_types["File"] = true;
      output_types["Console"] = true;

      // Assignment types
      assignment_types["DivideEquals"] = true;
      assignment_types["TimesEquals"] = true;
      assignment_types["PlusEquals"] = true;
      assignment_types["MinusEquals"] = true;
      assignment_types["Assignment"] = true;

      // standalone types
      standalone_types["For"] = true;
      standalone_types["While"] = true;
      standalone_types["If"] = true;
      standalone_types["FunctionCall"] = true;

      // accumulator types
      accumulator_types["Increment"] = true;
      accumulator_types["Decrement"] = true;

      // accumulator types
      connection_types["AssignmentConnection"] = true;
      connection_types["ArgumentConnection"] = true;
      connection_types["AssignmentRHSConnection"] = true;
      connection_types["ArrayAssignmentConnection"] = true;
      connection_types["OutputConnection"] = true;
      connection_types["IndexConnection"] = true;
      connection_types["NonModifyingConnection"] = true;
      connection_types["KeyboardConnection"] = true;


      // boolean operator types
      boolean_operator_types["And"] = true;
      boolean_operator_types["Or"] = true;
      boolean_operator_types["Not"] = true;
      boolean_operator_types["Equal"] = true;
      boolean_operator_types["NotEqual"] = true;
      boolean_operator_types["LessThan"] = true;
      boolean_operator_types["LessThanOrEqual"] = true;
      boolean_operator_types["GreaterThan"] = true;
      boolean_operator_types["GreaterThanOrEqual"] = true;

      initialized_ = true;
    }
}

GOAL::Grammar::Grammar ()
{
  init ();
}

bool 
GOAL::Grammar::isLhs (const Uml::Class & object)
{
  init ();
  return lhs_types.find (object.name ()) != lhs_types.end ();
}

bool 
GOAL::Grammar::isRhs (const Uml::Class & object) 
{
  init ();
  return rhs_types.find (object.name ()) != rhs_types.end ();
}

bool 
GOAL::Grammar::isAssignment (const Uml::Class & object) 
{
  init ();
  return assignment_types.find (object.name ()) != assignment_types.end ();
}

bool 
GOAL::Grammar::isConnection (const Uml::Class & object) 
{
  init ();
  return connection_types.find (object.name ()) != connection_types.end ();
}

bool 
GOAL::Grammar::isAccumulator (const Uml::Class & object) 
{
  init ();
  return accumulator_types.find (object.name ()) != accumulator_types.end ();
}

bool 
GOAL::Grammar::isBooleanOperator (const Uml::Class & object) 
{
  init ();
  return boolean_operator_types.find (object.name ()) != boolean_operator_types.end ();
}

bool 
GOAL::Grammar::isStandalone (const Uml::Class & object) 
{
  init ();
  return standalone_types.find (object.name ()) != standalone_types.end ();
}

bool 
GOAL::Grammar::isInput (const Uml::Class & object) 
{
  init ();
  return input_types.find (object.name ()) != input_types.end ();
}

bool 
GOAL::Grammar::isOutput (const Uml::Class & object) 
{
  init ();
  return output_types.find (object.name ()) != output_types.end ();
}

bool 
GOAL::Grammar::isClass (const Uml::Class & object) 
{
  init ();
  return object == GOAL::Class::meta || object == GOAL::ClassAdapter::meta;
}

bool 
GOAL::Grammar::isAssignmentLine (Code_Line & line, Info& info) 
{
  bool outcome = false;
  info.confidence = 0;
  info.pos = line.end ();

  Code_Line::iterator assign (line.begin ());

  // we can't use find because there are several types of assignment
  for (; assign != line.end (); ++assign)
    {
      if (isAssignment ((*assign).type ()))
        {
          // pretty sure this is an assignment
          info.confidence = 80;
          outcome = true;
          break;
        }
    }

  if (assign == line.end ())
    return outcome;

  // the Info object's pos should point to assign, since it is the
  // landmark terminal

  info.pos = assign;

  // setup lhs iterator at beginning
  Code_Line::iterator lhs (line.begin ());
  bool lhsCorrect = true;

  for (; lhs != assign; ++lhs)
    {
       if (!isLhs( (*lhs).type ()))
         {
            lhsCorrect = false;
         }
    }
  
  // if everything to the left of the assignment operator
  // fell under the category of a left-hand side element, then
  // improve the confidence rating
  if (lhsCorrect)
    info.confidence += 12; 

  // setup rhs iterator at assignment + 1
  Code_Line::iterator rhs (assign);

  bool rhsCorrect = true;

  for (++rhs; rhs != line.end (); ++rhs)
    {
       if (!isRhs( (*rhs).type ()))
         {
            rhsCorrect = false;
         }
    }
  
  if (rhsCorrect)
    info.confidence += 8; 


  return outcome;
  // check the section up to 
}

bool 
GOAL::Grammar::isFunctionCall (Code_Line & line, Info& info) 
{
  bool outcome = false;
  info.confidence = 0;
  info.pos = line.begin ();

  Code_Line::iterator first (info.pos);

  if ("FunctionCall" == (*first).type ().name ().operator std::string ())
    {
      info.confidence = 90;
      info.pos = first;
      outcome = true;
    }

  if (line.size () == 1)
    info.confidence += 10;

  return outcome;
}

bool 
GOAL::Grammar::isConsolePrintout (Code_Line & line, Info& info) 
{
  bool outcome = false;
  info.confidence = 0;
  info.pos = line.begin ();

  Code_Line::iterator assign (info.pos);
  Code_Line::iterator first (line.begin ());
  Code_Line::iterator expected (line.begin ());
  ++expected;

  // we can't use find because there are several types of assignment
  for (; assign != line.end (); ++assign)
    {
      if (isAssignment ((*assign).type ()))
        {
          // pretty sure this is an assignment
          info.confidence = 10;
          break;
        }
    }


  if (assign != expected)
    {
      outcome = false;
      return outcome;
    }

  if ("Console" == (*first).type ().name ().operator std::string ())
    {
      // this is a console printout
      info.confidence = 100;
      outcome = true;

      info.pos = assign;
    }

  return outcome;
}

bool 
GOAL::Grammar::isInputLine (Code_Line & line, Info& info) 
{
  bool outcome = false;
  info.confidence = 0;
  info.pos = line.begin ();

  Code_Line::iterator assign (info.pos);
  Code_Line::iterator first (line.begin ());
  Code_Line::iterator expected (line.begin ());
  ++expected;

  // we can't use find because there are several types of assignment
  for (; assign != line.end (); ++assign)
    {
      if (isAssignment ((*assign).type ()))
        {
          // pretty sure this is an assignment
          info.confidence = 10;
          break;
        }
    }

  if (assign == line.end ())
    return false;

  info.pos = assign;
  
  // setup lhs iterator at beginning
  Code_Line::iterator lhs (line.begin ());
  bool lhsCorrect = true;

  for (; lhs != assign; ++lhs)
    {
       if (!isLhs( (*lhs).type ()))
         {
            lhsCorrect = false;
         }
    }

  // if everything to the left of the assignment operator
  // fell under the category of a left-hand side element, then
  // improve the confidence rating
  if (lhsCorrect)
    info.confidence += 12; 

  
  // setup lhs iterator at beginning
  Code_Line::iterator rhs = assign;
  ++rhs;
  bool rhsCorrect = true;

  if (rhs != line.end ())
    {
      if (isInput ( (*rhs).type ()))
        {
          // this is a console printout
          info.confidence += 70;
          outcome = true;

          info.pos = assign;
        }
    }

  ++rhs;

  if (rhs == line.end ())
    info.confidence += 8;

  return outcome;
}

#endif